home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 365_01 / ctags.c < prev    next >
C/C++ Source or Header  |  1992-04-04  |  19KB  |  820 lines

  1. /* ctags.c */
  2.  
  3. /* This is a reimplementation of the ctags(1) program.  It supports ANSI C,
  4.  * and has heaps o' flags.  It is meant to be distributed with elvis.
  5.  */
  6.  
  7. #include <stdio.h>
  8. #include "config.h"
  9. #ifndef FALSE
  10. # define FALSE    0
  11. # define TRUE    1
  12. #endif
  13. #ifndef TAGS
  14. # define TAGS    "tags"
  15. #endif
  16. #ifndef REFS
  17. # define REFS    "refs"
  18. #endif
  19. #ifndef BLKSIZE
  20. # define BLKSIZE 1024
  21. #endif
  22.  
  23. #include "ctype.c" /* yes, that really is the .c file, not the .h one. */
  24.  
  25. /* -------------------------------------------------------------------------- */
  26. /* Some global variables */
  27.  
  28. /* The following boolean variables are set according to command line flags */
  29. int    incl_static;    /* -s  include static tags */
  30. int    incl_types;    /* -t  include typedefs and structs */
  31. int    incl_vars;    /* -v  include variables */
  32. int    make_refs;    /* -r  generate a "refs" file */
  33. int    append_files;    /* -a  append to "tags" [and "refs"] files */
  34.  
  35. /* The following are used for outputting to the "tags" and "refs" files */
  36. FILE    *tags;        /* used for writing to the "tags" file */
  37. FILE    *refs;        /* used for writing to the "refs" file */
  38.  
  39. /* -------------------------------------------------------------------------- */
  40. /* These are used for reading a source file.  It keeps track of line numbers */
  41. char    *file_name;    /* name of the current file */
  42. FILE    *file_fp;    /* stream used for reading the file */
  43. long    file_lnum;    /* line number in the current file */
  44. long    file_seek;    /* fseek() offset to the start of current line */
  45. int    file_afternl;    /* boolean: was previous character a newline? */
  46. int    file_prevch;    /* a single character that was ungotten */
  47. int    file_header;    /* boolean: is the current file a header file? */
  48.  
  49. /* This function opens a file, and resets the line counter.  If it fails, it
  50.  * it will display an error message and leave the file_fp set to NULL.
  51.  */
  52. void file_open(name)
  53.     char    *name;    /* name of file to be opened */
  54. {
  55.     /* if another file was already open, then close it */
  56.     if (file_fp)
  57.     {
  58.         fclose(file_fp);
  59.     }
  60.  
  61.     /* try to open the file for reading.  The file must be opened in
  62.      * "binary" mode because otherwise fseek() would misbehave under DOS.
  63.      */
  64. #if MSDOS || TOS
  65.     file_fp = fopen(name, "rb");
  66. #else
  67.     file_fp = fopen(name, "r");
  68. #endif
  69.     if (!file_fp)
  70.     {
  71.         perror(name);
  72.     }
  73.  
  74.     /* reset the name & line number */
  75.     file_name = name;
  76.     file_lnum = 0L;
  77.     file_seek = 0L;
  78.     file_afternl = TRUE;
  79.  
  80.     /* determine whether this is a header file */
  81.     file_header = FALSE;
  82.     name += strlen(name) - 2;
  83.     if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
  84.     {
  85.         file_header = TRUE;
  86.     }
  87. }
  88.  
  89. /* This function reads a single character from the stream.  If the *previous*
  90.  * character was a newline, then it also increments file_lnum and sets
  91.  * file_offset.
  92.  */
  93. int file_getc()
  94. {
  95.     int    ch;
  96.  
  97.     /* if there is an ungotten character, then return it.  Don't do any
  98.      * other processing on it, though, because we already did that the
  99.      * first time it was read.
  100.      */
  101.     if (file_prevch)
  102.     {
  103.         ch = file_prevch;
  104.         file_prevch = 0;
  105.         return ch;
  106.     }
  107.  
  108.     /* if previous character was a newline, then we're starting a line */
  109.     if (file_afternl)
  110.     {
  111.         file_afternl = FALSE;
  112.         file_seek = ftell(file_fp);
  113.         file_lnum++;
  114.     }
  115.  
  116.     /* Get a character.  If no file is open, then return EOF */
  117.     ch = (file_fp ? getc(file_fp) : EOF);
  118.  
  119.     /* if it is a newline, then remember that fact */
  120.     if (ch == '\n')
  121.     {
  122.         file_afternl = TRUE;
  123.     }
  124.  
  125.     /* return the character */
  126.     return ch;
  127. }
  128.  
  129. /* This function ungets a character from the current source file */
  130. void file_ungetc(ch)
  131.     int    ch;    /* character to be ungotten */
  132. {
  133.     file_prevch = ch;
  134. }
  135.  
  136. /* This function copies the current line out some other fp.  It has no effect
  137.  * on the file_getc() function.  During copying, any '\' characters are doubled
  138.  * and a leading '^' or trailing '$' is also quoted.  The newline character is
  139.  * not copied.
  140.  *
  141.  * This is meant to be used when generating a tag line.
  142.  */
  143. void file_copyline(seek, fp)
  144.     long    seek;    /* where the lines starts in the source file */
  145.     FILE    *fp;    /* the output stream to copy it to */
  146. {
  147.     long    oldseek;/* where the file's pointer was before we messed it up */
  148.     char    ch;    /* a single character from the file */
  149.     char    next;    /* the next character from this file */
  150.  
  151.     /* go to the start of the line */
  152.     oldseek = ftell(file_fp);
  153.     fseek(file_fp, seek, 0);
  154.  
  155.     /* if first character is '^', then emit \^ */
  156.     ch = getc(file_fp);
  157.     if (ch == '^')
  158.     {
  159.         putc('\\', fp);
  160.         putc('^', fp);
  161.         ch = getc(file_fp);
  162.     }
  163.  
  164.     /* write everything up to, but not including, the newline */
  165.     while (ch != '\n')
  166.     {
  167.         /* preread the next character from this file */
  168.         next = getc(file_fp);
  169.  
  170.         /* if character is '\', or a terminal '$', then quote it */
  171.         if (ch == '\\' || (ch == '$' && next == '\n'))
  172.         {
  173.             putc('\\', fp);
  174.         }
  175.         putc(ch, fp);
  176.  
  177.         /* next character... */
  178.         ch = next;
  179.     }
  180.  
  181.     /* seek back to the old position */
  182.     fseek(file_fp, oldseek, 0);
  183. }
  184.  
  185. /* -------------------------------------------------------------------------- */
  186. /* This section handles preprocessor directives.  It strips out all of the
  187.  * directives, and may emit a tag for #define directives.
  188.  */
  189.  
  190. int    cpp_afternl;    /* boolean: look for '#' character? */
  191. int    cpp_prevch;    /* an ungotten character, if any */
  192. int    cpp_refsok;    /* boolean: can we echo characters out to "refs"? */
  193.  
  194. /* This function opens the file & resets variables */
  195. void cpp_open(name)
  196.     char    *name;    /* name of source file to be opened */
  197. {
  198.     /* use the lower-level file_open function to open the file */
  199.     file_open(name);
  200.  
  201.     /* reset variables */
  202.     cpp_afternl = TRUE;
  203.     cpp_refsok = TRUE;
  204. }
  205.  
  206. /* This function copies a character from the source file to the "refs" file */
  207. void cpp_echo(ch)
  208.     int    ch; /* the character to copy */
  209. {
  210.     static    wasnl;
  211.  
  212.     /* echo non-EOF chars, unless not making "ref", or echo turned off */
  213.     if (ch != EOF && make_refs && cpp_refsok && !file_header)
  214.     {
  215.         /* try to avoid blank lines */
  216.         if (ch == '\n')
  217.         {
  218.             if (wasnl)
  219.             {
  220.                 return;
  221.             }
  222.             wasnl = TRUE;
  223.         }
  224.         else
  225.         {
  226.             wasnl = FALSE;
  227.         }
  228.  
  229.         /* add the character */
  230.         putc(ch, refs);
  231.     }
  232. }
  233.  
  234. /* This function returns the next character which isn't part of a directive */
  235. int cpp_getc()
  236. {
  237.     static
  238.     int    ch;    /* the next input character */
  239.     char    *scan;
  240.  
  241.     /* if we have an ungotten character, then return it */
  242.     if (cpp_prevch)
  243.     {
  244.         ch = cpp_prevch;
  245.         cpp_prevch = 0;
  246.         return ch;
  247.     }
  248.  
  249.     /* Get a character from the file.  Return it if not special '#' */
  250.     ch = file_getc();
  251.     if (ch == '\n')
  252.     {
  253.         cpp_afternl = TRUE;
  254.         cpp_echo(ch);
  255.         return ch;
  256.     }
  257.     else if (ch != '#' || !cpp_afternl)
  258.     {
  259.         /* normal character.  Any non-whitespace should turn off afternl */
  260.         if (ch != ' ' && ch != '\t')
  261.         {
  262.             cpp_afternl = FALSE;
  263.         }
  264.         cpp_echo(ch);
  265.         return ch;
  266.     }
  267.  
  268.     /* Yikes!  We found a directive */
  269.  
  270.     /* see whether this is a #define line */
  271.     scan = " define ";
  272.     while (*scan)
  273.     {
  274.         if (*scan == ' ')
  275.         {
  276.             /* space character matches any whitespace */
  277.             do
  278.             {
  279.                 ch = file_getc();
  280.             } while (ch == ' ' || ch == '\t');
  281.             file_ungetc(ch);
  282.         }
  283.         else
  284.         {
  285.             /* other characters should match exactly */
  286.             ch = file_getc();
  287.             if (ch != *scan)
  288.             {
  289.                 file_ungetc(ch);
  290.                 break;
  291.             }
  292.         }
  293.         scan++;
  294.     }
  295.  
  296.     /* is this a #define line?  and should we generate a tag for it? */
  297.     if (!*scan && (file_header || incl_static))
  298.     {
  299.         /* if not a header, then this will be a static tag */
  300.         if (!file_header)
  301.         {
  302.             fputs(file_name, tags);
  303.             putc(':', tags);
  304.         }
  305.  
  306.         /* output the tag name */
  307.         for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc())
  308.         {
  309.             putc(ch, tags);
  310.         }
  311.  
  312.         /* output a tab, the filename, another tab, and the line number */
  313.         fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum);
  314.     }
  315.  
  316.     /* skip to the end of the directive -- a newline that isn't preceded
  317.      * by a '\' character.
  318.      */
  319.     while (ch != EOF && ch != '\n')
  320.     {
  321.         if (ch == '\\')
  322.         {
  323.             ch = file_getc();
  324.         }
  325.         ch = file_getc();
  326.     }
  327.  
  328.     /* return the newline that we f